package data

import (
	"context"
	"fmt"
	"io"
	"slices"
	"strconv"
	"strings"
	"time"

	"github.com/moby/moby/api/types/container"
	"github.com/moby/moby/api/types/network"
	"github.com/moby/moby/client"

	"github.com/acepanel/panel/internal/biz"
	"github.com/acepanel/panel/internal/http/request"
	"github.com/acepanel/panel/pkg/types"
)

type containerRepo struct{}

func NewContainerRepo() biz.ContainerRepo {
	return &containerRepo{}
}

// ListAll 列出所有容器
func (r *containerRepo) ListAll() ([]types.Container, error) {
	apiClient, err := getDockerClient("/var/run/docker.sock")
	if err != nil {
		return nil, err
	}
	defer func(apiClient *client.Client) { _ = apiClient.Close() }(apiClient)

	resp, err := apiClient.ContainerList(context.Background(), client.ContainerListOptions{
		All: true,
	})
	if err != nil {
		return nil, err
	}

	var containers []types.Container
	for _, item := range resp.Items {
		ports := make([]types.ContainerPort, 0)
		for _, port := range item.Ports {
			ports = append(ports, types.ContainerPort{
				ContainerStart: uint(port.PrivatePort),
				ContainerEnd:   uint(port.PrivatePort),
				HostStart:      uint(port.PublicPort),
				HostEnd:        uint(port.PublicPort),
				Protocol:       port.Type,
				Host:           port.IP,
			})
		}
		if len(item.Names) == 0 {
			item.Names = append(item.Names, "")
		}
		containers = append(containers, types.Container{
			ID:        item.ID,
			Name:      strings.TrimPrefix(item.Names[0], "/"), // https://github.com/moby/moby/issues/7519
			Image:     item.Image,
			ImageID:   item.ImageID,
			Command:   item.Command,
			CreatedAt: time.Unix(item.Created, 0),
			State:     string(item.State),
			Status:    item.Status,
			Ports:     ports,
			Labels:    types.MapToKV(item.Labels),
		})
	}

	slices.SortFunc(containers, func(a types.Container, b types.Container) int {
		return strings.Compare(a.Name, b.Name)
	})

	return containers, nil
}

// ListByName 根据名称搜索容器
func (r *containerRepo) ListByName(names string) ([]types.Container, error) {
	containers, err := r.ListAll()
	if err != nil {
		return nil, err
	}

	containers = slices.DeleteFunc(containers, func(item types.Container) bool {
		return !strings.Contains(item.Name, names)
	})

	return containers, nil
}

// Create 创建容器
func (r *containerRepo) Create(req *request.ContainerCreate) (string, error) {
	apiClient, err := getDockerClient("/var/run/docker.sock")
	if err != nil {
		return "", err
	}
	defer func(apiClient *client.Client) { _ = apiClient.Close() }(apiClient)

	ctx := context.Background()

	// 拉取镜像
	out, err := apiClient.ImagePull(ctx, req.Name, client.ImagePullOptions{})
	if err != nil {
		return "", err
	}
	defer func(out client.ImagePullResponse) { _ = out.Close() }(out)

	// TODO 实现流式显示拉取进度
	if err = out.Wait(ctx); err != nil {
		return "", err
	}

	// 构建容器配置
	config := &container.Config{
		Image:        req.Image,
		Tty:          req.Tty,
		OpenStdin:    req.OpenStdin,
		AttachStdin:  req.OpenStdin,
		AttachStdout: true,
		AttachStderr: true,
		Env:          types.KVToSlice(req.Env),
		Labels:       types.KVToMap(req.Labels),
		Entrypoint:   req.Entrypoint,
		Cmd:          req.Command,
	}

	// 构建主机配置
	hostConfig := &container.HostConfig{
		AutoRemove:      req.AutoRemove,
		Privileged:      req.Privileged,
		PublishAllPorts: req.PublishAllPorts,
	}

	// 构建网络配置
	networkConfig := &network.NetworkingConfig{}
	if req.Network != "" {
		switch req.Network {
		case "host", "none", "bridge":
			hostConfig.NetworkMode = container.NetworkMode(req.Network)
		}
		networkConfig.EndpointsConfig = map[string]*network.EndpointSettings{req.Network: {}}
	}

	// 设置端口映射
	if !req.PublishAllPorts && len(req.Ports) > 0 {
		portMap := make(network.PortMap)
		for _, port := range req.Ports {
			if port.ContainerStart-port.ContainerEnd != port.HostStart-port.HostEnd {
				return "", fmt.Errorf("容器端口和主机端口数量不匹配（容器: %d 主机: %d）", port.ContainerStart-port.ContainerEnd, port.HostStart-port.HostEnd)
			}
			if port.ContainerStart > port.ContainerEnd || port.HostStart > port.HostEnd || port.ContainerStart < 1 || port.HostStart < 1 {
				return "", fmt.Errorf("端口范围不正确")
			}

			count := uint(0)
			for i := port.HostStart; i <= port.HostEnd; i++ {
				bindItem := network.PortBinding{HostIP: port.Host, HostPort: strconv.Itoa(int(i))}
				portMap[network.MustParsePort(fmt.Sprintf("%d/%s", port.ContainerStart+count, port.Protocol))] = []network.PortBinding{bindItem}
				count++
			}
		}

		exposed := make(network.PortSet)
		for port := range portMap {
			exposed[port] = struct{}{}
		}

		config.ExposedPorts = exposed
		hostConfig.PortBindings = portMap
	}
	// 设置卷挂载
	volumes := make(map[string]struct{})
	for _, v := range req.Volumes {
		volumes[v.Container] = struct{}{}
		hostConfig.Binds = append(hostConfig.Binds, fmt.Sprintf("%s:%s:%s", v.Host, v.Container, v.Mode))
	}
	config.Volumes = volumes
	// 设置重启策略
	hostConfig.RestartPolicy = container.RestartPolicy{Name: container.RestartPolicyMode(req.RestartPolicy)}
	if req.RestartPolicy == "on-failure" {
		hostConfig.RestartPolicy.MaximumRetryCount = 5
	}
	// 设置资源限制
	hostConfig.CPUShares = req.CPUShares
	hostConfig.NanoCPUs = req.CPUs * 1e9
	hostConfig.Memory = req.Memory * 1024 * 1024
	hostConfig.MemorySwap = 0

	// 创建容器
	resp, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{
		Name:             req.Name,
		Config:           config,
		HostConfig:       hostConfig,
		NetworkingConfig: networkConfig,
	})
	if err != nil {
		return "", err
	}

	// 启动容器
	_, _ = apiClient.ContainerStart(ctx, resp.ID, client.ContainerStartOptions{})
	return resp.ID, nil
}

// Remove 移除容器
func (r *containerRepo) Remove(id string) error {
	apiClient, err := getDockerClient("/var/run/docker.sock")
	if err != nil {
		return err
	}
	defer func(apiClient *client.Client) { _ = apiClient.Close() }(apiClient)

	_, err = apiClient.ContainerRemove(context.Background(), id, client.ContainerRemoveOptions{
		Force: true,
	})
	return err
}

// Start 启动容器
func (r *containerRepo) Start(id string) error {
	apiClient, err := getDockerClient("/var/run/docker.sock")
	if err != nil {
		return err
	}
	defer func(apiClient *client.Client) { _ = apiClient.Close() }(apiClient)

	_, err = apiClient.ContainerStart(context.Background(), id, client.ContainerStartOptions{})
	return err
}

// Stop 停止容器
func (r *containerRepo) Stop(id string) error {
	apiClient, err := getDockerClient("/var/run/docker.sock")
	if err != nil {
		return err
	}
	defer func(apiClient *client.Client) { _ = apiClient.Close() }(apiClient)

	_, err = apiClient.ContainerStop(context.Background(), id, client.ContainerStopOptions{})
	return err
}

// Restart 重启容器
func (r *containerRepo) Restart(id string) error {
	apiClient, err := getDockerClient("/var/run/docker.sock")
	if err != nil {
		return err
	}
	defer func(apiClient *client.Client) { _ = apiClient.Close() }(apiClient)

	_, err = apiClient.ContainerRestart(context.Background(), id, client.ContainerRestartOptions{})
	return err
}

// Pause 暂停容器
func (r *containerRepo) Pause(id string) error {
	apiClient, err := getDockerClient("/var/run/docker.sock")
	if err != nil {
		return err
	}
	defer func(apiClient *client.Client) { _ = apiClient.Close() }(apiClient)

	_, err = apiClient.ContainerPause(context.Background(), id, client.ContainerPauseOptions{})
	return err
}

// Unpause 恢复容器
func (r *containerRepo) Unpause(id string) error {
	apiClient, err := getDockerClient("/var/run/docker.sock")
	if err != nil {
		return err
	}
	defer func(apiClient *client.Client) { _ = apiClient.Close() }(apiClient)

	_, err = apiClient.ContainerUnpause(context.Background(), id, client.ContainerUnpauseOptions{})
	return err
}

// Kill 杀死容器
func (r *containerRepo) Kill(id string) error {
	apiClient, err := getDockerClient("/var/run/docker.sock")
	if err != nil {
		return err
	}
	defer func(apiClient *client.Client) { _ = apiClient.Close() }(apiClient)

	_, err = apiClient.ContainerKill(context.Background(), id, client.ContainerKillOptions{
		Signal: "KILL",
	})
	return err
}

// Rename 重命名容器
func (r *containerRepo) Rename(id string, newName string) error {
	apiClient, err := getDockerClient("/var/run/docker.sock")
	if err != nil {
		return err
	}
	defer func(apiClient *client.Client) { _ = apiClient.Close() }(apiClient)

	_, err = apiClient.ContainerRename(context.Background(), id, client.ContainerRenameOptions{
		NewName: newName,
	})
	return err
}

// Logs 查看容器日志
func (r *containerRepo) Logs(id string) (string, error) {
	apiClient, err := getDockerClient("/var/run/docker.sock")
	if err != nil {
		return "", err
	}
	defer func(apiClient *client.Client) { _ = apiClient.Close() }(apiClient)

	reader, err := apiClient.ContainerLogs(context.Background(), id, client.ContainerLogsOptions{
		ShowStdout: true,
		ShowStderr: true,
		Tail:       "100",
	})
	if err != nil {
		return "", err
	}
	defer func(reader io.ReadCloser) { _ = reader.Close() }(reader)

	logs, err := io.ReadAll(reader)
	if err != nil {
		return "", err
	}

	return string(logs), nil
}

// Prune 清理未使用的容器
func (r *containerRepo) Prune() error {
	apiClient, err := getDockerClient("/var/run/docker.sock")
	if err != nil {
		return err
	}
	defer func(apiClient *client.Client) { _ = apiClient.Close() }(apiClient)

	_, err = apiClient.ContainerPrune(context.Background(), client.ContainerPruneOptions{})
	return err
}
